/****************************************************************************
 名称：地理资源库管理
 作者：冯功耀
 功能描述：
 使用方式：
 可能存在的 BUG 或注意事项：
 最后修改日期：2025-06-07
 ****************************************************************************/


import webSocketTool from '../../src/com/webSocketTool/webSocketTool';
import FSTool from '../../src/lib/FSTool';
import path from 'path';
import stringMd5 from '../../src/lib/FSTool/stringMd5';
import configTool from '../../src/com/configTool';
import DBTool from './DBTool/DBTool';
import TileSet from '../lib/TileTool/TileSet';
import DBConnectTool from './DBTool/DBConnectTool';
// @ts-ignore
import colors from 'colors-console';
import chunkedParseJson from '../utils/chunkedParseJson';

const watch = require('node-watch');

type indexData = {
    tile2D: Map<string, TileSet>,
    tile3D: Set<string>,
    terrain: Set<string>,
}


const MapResource = {
    currentDirFileCount: 0,
    currentDirFileMetadata: {
        tile2D: new Map(),
        tile3D: new Set(),
        terrain: new Set()
    },
    mapResourcesCache: null,
    dbDataIndexList: new Map(),
    openWatcher: false,


    async getMapResources(readFromCache = true) {
        await configTool.init();

        if (readFromCache && this.mapResourcesCache) {
            // 若从缓存中读取的。则会自动开启文件夹监听，以保持数据的完整性
            await this.onWatcher();
            return this.mapResourcesCache;
        }

        const data = new Date().getTime();
        const dataIndex = this.readFileFromDisk();
        const dataIndexes = await this.readFileFromDB();
        const sum = this.sumDataIndexes([dataIndex, dataIndexes]);
        const mapResources = this.format(sum);
        this.mapResourcesCache = mapResources;

        const passTime = new Date().getTime() - data;


        console.log(colors('green', `检索资源库地图资源，搜索用时：${passTime} 毫秒，共搜索到：${mapResources.length}个地图资源`));
        return mapResources;
    },


    async onWatcher() {
        if (this.openWatcher === false && configTool.wwwPath) {
            console.log(colors('blue', '已开启【MapResources】地理资源文件夹监听'));
            this.openWatcher = true;
            await this.getMapResources();
            let timeout: string | number | NodeJS.Timeout | undefined;
            watch(path.join(configTool.wwwPath, '/MapResources'),
                {recursive: true}, (active: string, filePath: string) => {
                    clearTimeout(timeout); // 清除之前的计时器

                    const oldMapDataLength = this.mapResourcesCache.length;
                    timeout = setTimeout(async () => {
                        let mapResources = await this.getMapResources();
                        const newMapDataLength = mapResources.length;
                        if (oldMapDataLength !== newMapDataLength) {
                            this.sendMapResourcesToWS(mapResources);
                        }
                    }, 500); // 设置 500 ms延迟
                });
        }
    },

    decodePathList(pathList: string[]) {
        const dataPathIndex = {
            tile2D: new Map(),
            tile3D: new Set(),
            terrain: new Set()
        };

        // 判断是否是瓦片的正则表达式
        const tile2DRegex = /\\([0-9]+)\\([0-9]+)\\([0-9]+)\.(png|jpg|jpeg|webp)$/;
        for (let i = 0; i < pathList.length; i++) {
            const dataPath = pathList [i];
            if (tile2DRegex.test(dataPath)) {
                // 使用正则表达式替换数字部分为占位符
                const convertedPath = dataPath.replace(tile2DRegex, '\\{z}\\{x}\\{y}.$4').replace(/\\/g, '/');

                if (dataPathIndex.tile2D.has(convertedPath)) {
                    const tileSet = dataPathIndex.tile2D.get(convertedPath);
                    const match = dataPath.match(tile2DRegex);
                    if (match) {
                        const [z, x, y] = match.slice(1).map(Number);
                        tileSet.add(x, y, z);
                    }
                } else {
                    dataPathIndex.tile2D.set(convertedPath, new TileSet());
                }
            } else if (dataPath.endsWith('tileset.json')) {
                const tile3DPath = path.join(dataPath).replace(/\\/g, '/');
                dataPathIndex.tile3D.add(tile3DPath);
            } else if (dataPath.endsWith('layer.json')) {
                const terrainPath = dataPath.replace(/\\/g, '/');
                dataPathIndex.terrain.add(terrainPath);
            }
        }

        // 遍历 tile2D 并输出结果
        for (const [convertedPath, tileSet] of dataPathIndex.tile2D) {
            const layerProperties = tileSet.getLayerProperties();
            dataPathIndex.tile2D.set(convertedPath, layerProperties);
        }


        return dataPathIndex;
    },

    readFileFromDisk() {
        const basePath = path.join(configTool.wwwPath, '/MapResources');
        const fileList = FSTool.getFilePathList(basePath)
            .map((item: string) => item.replace(configTool.wwwPath, ''))
            .filter((item: string) => !item.includes('Exclude'));

        const fileCount = fileList.length;
        if (fileCount !== this.currentDirFileCount) {
            this.currentDirFileMetadata = this.decodePathList(fileList);
            this.currentDirFileCount = fileCount;
        }

        return this.currentDirFileMetadata;
    },

    async readFileFromDB() {
        let dbList = await DBTool.getDBInfoList();
        dbList = dbList.filter(item => !item.filePath.includes('Exclude'));
        const listMd5 = stringMd5(JSON.stringify(dbList.map((item: any) => {
            return {id: item.id};
        })));
        if (this.dbDataIndexList.has(listMd5)) {
            return this.dbDataIndexList.get(listMd5);
        }


        const dataIndexList = [];
        for (let i = 0; i < dbList.length; i++) {
            const dbItem = dbList[i];
            const dbName = dbItem.fileName;


            let metaData = dbItem.mataData;

            let dataIndex = null;
            if (metaData.dataIndex) {
                dataIndex = metaData.dataIndex;
                if (typeof dataIndex === 'string') {
                    try {
                        dataIndex = await chunkedParseJson(dataIndex);
                    } catch (e) {
                        console.log(colors('red', `MapResource-数据索引错误！！请检查数据索引文件长度是否正常！！`));
                        dataIndex = null;
                    }
                }
            }

            if (!dataIndex) {
                const connect = await DBConnectTool.openDB(dbName);
                const allFilePath = await connect.getAllFilePathList();
                dataIndex = MapResource.decodePathList(allFilePath.map((item: any) => {
                    return path.join('/DBService/' + dbName + item);
                }));
                dbItem.mataData.dataIndex = {
                    tile2D: Array.from(dataIndex.tile2D.entries()),
                    tile3D: Array.from(dataIndex.tile3D),
                    terrain: Array.from(dataIndex.terrain)
                };
                const res = await connect.setMataDataMapResources(dbItem.mataData.dataIndex);
                console.log(colors('blue', `MapResource- ${dbName} 数据索引为空，重建数据索引成功！` + res));
            }

            dataIndex && dataIndexList.push(dataIndex);
        }
        const sum = this.sumDataIndexes(dataIndexList);

        this.dbDataIndexList.set(listMd5, sum);
        return sum;
    },

    sumDataIndexes(dataIndexList: any[]) {
        const sum = {
            tile2D: new Map(),
            tile3D: new Set(),
            terrain: new Set()
        };
        for (let i = 0; i < dataIndexList.length; i++) {
            const dataIndex = dataIndexList[i];
            for (const [convertedPath, tileSet] of dataIndex.tile2D) {
                sum.tile2D.set(convertedPath, tileSet);
            }
            for (const tile3DPath of dataIndex.tile3D) {
                sum.tile3D.add(tile3DPath);
            }
            for (const terrainPath of dataIndex.terrain) {
                sum.terrain.add(terrainPath);
            }
        }
        return sum;
    },

    // 当地图资源发生变化时，发送更新消息给所有 WebSocket 客户端
    sendMapResourcesToWS(mapResources: any) {
        const mapResourcesMsg = JSON.stringify({
            bid: 'MapResourceList',
            data: mapResources,
            timestamp: new Date().getTime()
        }, null, 4);
        console.log(colors('magenta', '地图资源有更新，发送 WebSocket 同步信息到所有用户'));

        webSocketTool.sendToAllUser(mapResourcesMsg);
    },

    format(dataIndex: indexData) {
        const list = [];
        const ip = configTool.baseConfig.proxyAfterProxy_IPAddress;
        const webPort = configTool.baseConfig.proxyAfterProxy_Port;
        const proxyProtocol = configTool.baseConfig.proxyProtocol;

        for (const dataPath of dataIndex.terrain) {
            list.push({
                pid: stringMd5(dataPath),
                name: decodeURI(dataPath?.split('/')?.at(-2) || ''),
                catalog: '地形图层',
                dataType: 'terrain',
                showInTree: true,
                showInBox: true,
                defaultLoad: false,
                show: false,
                properties: {
                    scheme: 'CesiumTerrainProvider',
                    url: `${proxyProtocol}://${ip}:${webPort}` + dataPath.replace('/layer.json', '')
                }
            });
        }

        console.time('FindRoot');

        const tile3DSet = dataIndex.tile3D;
        const dirs = new Set<string>();

        // 提取所有目录
        for (const item of tile3DSet) {
            dirs.add(path.dirname(item));
        }

        const tile3DArr: string[] = [];

        // 找“不被其他任何目录作为前缀包含”的目录
        for (const item of tile3DSet) {
            let parent = path.dirname(item);
            let isRoot = true;

            // 向上检查每一级父目录
            while (parent !== '.' && parent !== '/') {
                parent = path.dirname(parent);
                if (dirs.has(parent)) {
                    isRoot = false;
                    break;
                }
            }

            if (isRoot) {
                tile3DArr.push(item);
            }
        }

        console.timeEnd('FindRoot');

        for (let i = 0; i < tile3DArr.length; i++) {
            const tilePath = tile3DArr[i];

            list.push({
                pid: stringMd5(tilePath),
                name: decodeURI(tilePath?.split('/')?.at(-2) || ''),
                catalog: '倾斜三维模型',
                dataType: 'Cesium3DTile',
                showInTree: true,
                showInBox: true,
                defaultLoad: false,
                show: false,
                properties: {
                    maximumScreenSpaceError: 4,
                    url: `${proxyProtocol}://${ip}:${webPort}` + tilePath
                }
            });
        }
        for (const [convertedPath, layerProperties] of dataIndex.tile2D) {
            if (!convertedPath.includes('NaturalEarthII')) {
                list.push({
                    pid: stringMd5(convertedPath),
                    name: decodeURI(convertedPath.split('/').at(-4) || ''),
                    catalog: '二维图层瓦片',
                    dataType: 'layer',
                    showInTree: true,
                    showInBox: true,
                    defaultLoad: false,
                    show: false,
                    properties: {
                        url: `${proxyProtocol}://${ip}:${webPort}` + convertedPath,
                        ...layerProperties
                    }
                });
            }
        }

        return list;
    }
};

export default MapResource;